package dsx.system;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import dsx.config.Config;
import dsx.config.ConfigItem;

public class DsxClassLoader {

	static HashMap<String, String> systemLoadedMap = new HashMap<String, String>();

	static HashMap<String, ClassLoader> customLoaderMap = new HashMap<String, ClassLoader>();

	static Object lockObj = new Object();

	public static void init() {
		synchronized (lockObj) {
			try {
				// 将已加载的jar包
				ArrayList<URL> urls = getUrls(ClassLoader
						.getSystemClassLoader());
				for (URL url : urls)
					systemLoadedMap.put(url.toURI().toString(), url.toURI()
							.toString());
				// 加载jar包
				ArrayList<String> frameLibs = getFrameLibs();
				for (String lib : frameLibs) {
					try {
						URL url = new File(lib).toURI().toURL();
						String fullPath = url.toURI().toString();
						if (systemLoadedMap.containsKey(fullPath))
							continue;
						// 加载本目录
						Class<URLClassLoader> sysclass = URLClassLoader.class;
						Method method = sysclass.getDeclaredMethod("addURL",
								new Class[] { URL.class });
						method.setAccessible(true);
						method.invoke(ClassLoader.getSystemClassLoader(), url);
						systemLoadedMap.put(fullPath, fullPath);
					} catch (Exception ex) {

					}
				}
			} catch (Exception ex) {
			}
		}
	}

	public static ClassLoader frameClassLoader() {
		return ClassLoader.getSystemClassLoader();
	}

	public static ClassLoader customClassLoader(String filePath) {
		synchronized (lockObj) {
			try {
				URL url = new File(filePath).toURI().toURL();
				String fullPath = url.toURI().toString();
				if (customLoaderMap.containsKey(fullPath))
					return customLoaderMap.get(fullPath);
				if (systemLoadedMap.containsKey(fullPath))
					return ClassLoader.getSystemClassLoader();

				URLClassLoader urlLoader = new URLClassLoader(new URL[] {},
						ClassLoader.getSystemClassLoader());
				HashMap<String, String> loadedMap = new HashMap<String, String>();
				loadedMap.putAll(systemLoadedMap);
				loadClassPath(urlLoader, filePath, loadedMap);

				customLoaderMap.put(fullPath, urlLoader);
				return urlLoader;
			} catch (Exception ex) {
				return null;
			}
		}
	}

	public static void freeCustomClassLoader(String filePath) {
		synchronized (lockObj) {
			try {
				URL url = new File(filePath).toURI().toURL();
				String fullPath = url.toURI().toString();
				URLClassLoader cl = (URLClassLoader) customLoaderMap
						.get(fullPath);
				if (cl != null) {
					customLoaderMap.remove(fullPath);
					cl.close();
				}
			} catch (Exception ex) {
			}
		}
	}

	private static ArrayList<URL> getUrls(ClassLoader loader) {
		try {
			ArrayList<URL> ret = new ArrayList<URL>();
			if (loader.getParent() != null) {
				ArrayList<URL> urls = getUrls(loader.getParent());
				if (urls != null)
					ret.addAll(urls);
			}
			URL[] urls = ((URLClassLoader) loader).getURLs();
			for (URL url : urls) {
				ret.add(url);
			}
			return ret;
		} catch (Exception ex) {
			return null;
		}
	}

	private static void loadClassPath(URLClassLoader urlLoader,
			String filePath, HashMap<String, String> ignore) {
		try {
			URL url = new File(filePath).toURI().toURL();
			String fullPath = url.toURI().toString();
			// 检查本目录是否被加载过
			if (ignore.containsKey(fullPath))
				return;
			// 加载本目录
			Class<URLClassLoader> sysclass = URLClassLoader.class;
			Method method = sysclass.getDeclaredMethod("addURL",
					new Class[] { URL.class });
			method.setAccessible(true);
			method.invoke(urlLoader, url);
			ignore.put(fullPath, fullPath);

			// 检查依赖，递归加载依赖项
			ArrayList<String> refPaths = getRefPaths(filePath);
			for (String refPath : refPaths) {
				loadClassPath(urlLoader, refPath, ignore);
			}
		} catch (Exception ex) {

		}
	}

	private static ArrayList<String> getRefPaths(String filePath) {
		ArrayList<String> refPaths = new ArrayList<String>();
		try {
			if (filePath.endsWith(".jar")) {
				JarFile jarfile = new JarFile(filePath);
				Manifest manifest = jarfile.getManifest();
				Attributes att = manifest.getMainAttributes();
				String classPaths = att.getValue("Class-Path");
				String[] paths = classPaths.split(" ");
				for (String path : paths) {
					if (!path.trim().isEmpty())
						refPaths.add(path.trim());
				}
				jarfile.close();
			} else {
				String classPathFile = "";
				if (filePath.endsWith("bin"))
					classPathFile = filePath
							.substring(0, filePath.length() - 3) + ".classpath";
				else if (filePath.endsWith("bin/"))
					classPathFile = filePath
							.substring(0, filePath.length() - 4) + ".classpath";
				if (!classPathFile.equals("")) {
					DocumentBuilderFactory dbf = DocumentBuilderFactory
							.newInstance();
					DocumentBuilder db = dbf.newDocumentBuilder();
					Document document = db.parse(new File(classPathFile));
					Element root = document.getDocumentElement();
					NodeList entryList = root
							.getElementsByTagName("classpathentry");
					for (int i = 0; i < entryList.getLength(); i++) {
						Element elem = (Element) entryList.item(i);
						if (elem.getAttribute("kind").equals("lib"))
							refPaths.add(elem.getAttribute("path"));
					}
				}
			}
		} catch (Exception ex) {
		}
		return refPaths;
	}

	private static ArrayList<String> getFrameLibs() {
		ArrayList<String> libs = new ArrayList<String>();
		try {
			// 框架库
			ArrayList<ArrayList<ConfigItem>> sections = Config
					.getSections("frameLib");
			if (sections != null && sections.size() > 0) {
				for (int h = 0; h < sections.size(); h++) {
					ArrayList<ConfigItem> fields = sections.get(h);
					String path = "";
					for (int i = 0; i < fields.size(); i++) {
						ConfigItem field = fields.get(i);
						if (field.name.equals("path")) {
							path = field.value;
							break;
						}
					}
					for (int i = 0; i < fields.size(); i++) {
						ConfigItem field = fields.get(i);
						if (field.name.equals("lib")) {
							if (!libs.contains(path + field.value))
								libs.add(path + field.value);
						}
					}
				}
			}

			// 扩展库
			sections = Config.getSections("extensionLib");
			if (sections != null && sections.size() > 0) {
				for (int h = 0; h < sections.size(); h++) {
					ArrayList<ConfigItem> fields = sections.get(h);
					String path = "";
					for (int i = 0; i < fields.size(); i++) {
						ConfigItem field = fields.get(i);
						if (field.name.equals("path")) {
							path = field.value;
							break;
						}
					}
					for (int i = 0; i < fields.size(); i++) {
						ConfigItem field = fields.get(i);
						if (field.name.equals("lib")) {
							if (!libs.contains(path + field.value))
								libs.add(path + field.value);
						}
					}
				}
			}
		} catch (Exception ex) {

		}
		return libs;
	}

}
